using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Data;
using System.IO;
using System.Diagnostics;

namespace MyClever.Lib
{
    public static class MissionXMLWriter
    {
        public static void WriteRootElement(ref MemoryStream missionXMLCode, ref Mission mission)
        {
            // reset the mission before to delete tempory cell values
            List<MissionPluginProxy> plugins = new List<MissionPluginProxy>();
            mission.PluginCollection.GetPlugins(ref plugins);
            foreach (MissionPluginProxy pluginOn in plugins)
	        {
                pluginOn.Reset();
	        } 
            
            XmlTextWriter xmlWriter = new XmlTextWriter(missionXMLCode, null);

            xmlWriter.Formatting = Formatting.Indented;
            xmlWriter.WriteStartDocument();
            xmlWriter.WriteStartElement("Mission");
            xmlWriter.WriteAttributeString("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");

            xmlWriter.WriteAttributeString("xsi:noNamespaceSchemaLocation", "Mission.xsd");
            MissionXMLWriter.WriteSpecifications(mission.Specification, ref xmlWriter);

            xmlWriter.WriteStartElement("Blueprint");
            WritePlugins(mission.PluginCollection, ref xmlWriter);
            xmlWriter.WriteEndElement(); // end "Blueprint"

            xmlWriter.WriteEndElement();
            xmlWriter.WriteEndDocument();
            xmlWriter.Flush();
        }

        private static void WriteSpecifications(Dictionary<string, string> specification, ref XmlTextWriter missionDocument)
        {           
            // run through all mission specifications ...
            missionDocument.WriteStartElement("Specification");    
            foreach (KeyValuePair<string, string> specificationOn in specification)
            {
                // ... and write them in the missionDocument
                missionDocument.WriteElementString(specificationOn.Key, specificationOn.Value);               
            }
            missionDocument.WriteEndElement(); // "Specification"
        }

        private static void WritePlugins(MissionPluginLoopNode pluginLoopNode, ref XmlTextWriter missionDocument)
        {
            foreach (MissionPluginNode pluginNodeOn in pluginLoopNode.PluginNodes)
	        {
                if (typeof(MissionPluginLoopNode) == pluginNodeOn.GetType())
                {
                    MissionPluginLoopNode pluginLoopOn = (MissionPluginLoopNode)pluginNodeOn;
                    
                    missionDocument.WriteStartElement("LoopThroughEveryPackageRow");
                    missionDocument.WriteAttributeString("FromPluginID", pluginLoopOn.PackageReference.PluginID.ToString());
                    missionDocument.WriteAttributeString("FromPackage", pluginLoopOn.PackageReference.PackageName);
                    missionDocument.WriteAttributeString("ID", pluginLoopOn.ID.ToString());

                    WritePlugins(pluginLoopOn, ref missionDocument);
                    
                    missionDocument.WriteEndElement(); // "LoopThroughEveryPackageRow"                    
                }
                else //MissionPluginProxy
                {
                    MissionPluginProxy pluginOn = (MissionPluginProxy)pluginNodeOn;

                    missionDocument.WriteStartElement("Plugin");
                    missionDocument.WriteAttributeString("Maintainer", (string)pluginOn.GetProperty(PluginProperty.Maintainer));
                    missionDocument.WriteAttributeString("Name", (string)pluginOn.GetProperty(PluginProperty.Name));
                    missionDocument.WriteAttributeString("Version", (string)pluginOn.GetProperty(PluginProperty.Version));
                    missionDocument.WriteAttributeString("File", pluginOn.Path.File);
                    missionDocument.WriteAttributeString("Class", pluginOn.ClassName);
                    missionDocument.WriteAttributeString("Language", pluginOn.ProgrammingLanguage.ToString());
                    missionDocument.WriteAttributeString("ID", pluginOn.ID.ToString());

                    // set all direction, we want to export in the missionxml-file
                    List<Direction> packageDirections = new List<Direction>();
                    packageDirections.Add(Direction.Input);
                    packageDirections.Add(Direction.Output);
                    // ... and loop through them
                    foreach (Direction directionOn in packageDirections)
                    {
                        // get all package from the given direction
                        missionDocument.WriteStartElement(directionOn.ToString());
                        foreach (MissionPluginPackage packageOn in pluginOn.GetPackages(directionOn))
                        {
                            if (true == packageOn.HasReference)
                            {
                                Debug.Assert(Direction.Input == directionOn);

                                missionDocument.WriteStartElement("LinkedPackage");
                                missionDocument.WriteAttributeString("Name", packageOn.TableName);
                                missionDocument.WriteAttributeString("FromPluginID", packageOn.Reference.PluginID.ToString());
                                missionDocument.WriteAttributeString("FromPackage", packageOn.Reference.PackageName);
                                missionDocument.WriteEndElement(); // "LinkedPackage"    
                            }
                            else if (Direction.Output == packageOn.Direction && true == packageOn.IsSoftPackage)
                            {
                                missionDocument.WriteStartElement("DynamicPackage");
                                missionDocument.WriteAttributeString("Name", packageOn.TableName);
                                missionDocument.WriteEndElement(); // "DynamicPackage"
                            }
                            else
                            {
                                missionDocument.WriteStartElement("Package");
                                missionDocument.WriteAttributeString("Name", packageOn.TableName);

                                // write the sequence description, if this package is a linked package 
                                if (true == packageOn.HasAutonomousSequence)
                                {
                                    // set the package-sequence definition
                                    missionDocument.WriteStartElement("Sequence");
                                    foreach (MissionPluginPackageColumn columnAttributeOn in packageOn.Columns)
                                    {
                                        missionDocument.WriteStartElement("Column");
                                        missionDocument.WriteAttributeString("Name", columnAttributeOn.ColumnName);
                                        missionDocument.WriteAttributeString("Type", columnAttributeOn.DataType.ToString());

                                        // is there any given default-value?
                                        if (false == String.IsNullOrEmpty(columnAttributeOn.DefaultValue.ToString()))
                                        {
                                            missionDocument.WriteAttributeString("DefaultValue", columnAttributeOn.DefaultValue.ToString());
                                        }

                                        missionDocument.WriteStartElement("Description");
                                        missionDocument.WriteString(columnAttributeOn.Description.ToString());
                                        missionDocument.WriteEndElement(); //"Description"

                                        // is there any given validation-value?
                                        if (true == columnAttributeOn.HasValidation)
                                        {
                                            missionDocument.WriteStartElement("Validation");
                                            missionDocument.WriteString(columnAttributeOn.Validation);
                                            missionDocument.WriteEndElement(); //"Validation"
                                        }
                                        missionDocument.WriteEndElement(); // "Column"
                                    }
                                    missionDocument.WriteEndElement(); // "Sequence"
                                }

                                // write the given data settings (only for input-packages)
                                if (Direction.Input == directionOn)
                                {
                                    Debug.Assert(false == packageOn.HasReference);
                                    missionDocument.WriteStartElement("Data");

                                    // write all given columns-references
                                    bool foundLinkedColumns = false;
                                    foreach (MissionPluginPackageColumn columnOn in packageOn.GetColumnsWithColumnReference())
                                    {
                                        // write the "LinkedColumns" only once, if there at least on columnReference!
                                        if (false == foundLinkedColumns)
                                        {
                                            missionDocument.WriteStartElement("LinkedColumns");
                                            foundLinkedColumns = true;
                                        }

                                        // the xml-element can't contain whitespaces ...
                                        missionDocument.WriteStartElement(columnOn.ColumnName.Replace(" ", ""));
                                        missionDocument.WriteAttributeString("FromPluginID", columnOn.ColumnReference.PluginID.ToString());
                                        missionDocument.WriteAttributeString("FromPackage", columnOn.ColumnReference.PackageName);
                                        missionDocument.WriteAttributeString("FromColumn", columnOn.ColumnReference.ColumnName);
                                        missionDocument.WriteEndElement(); // columnOn.ReferenceType.ToString()
                                    }
                                    // close "LinkedColumns" if needed
                                    if (true == foundLinkedColumns)
                                    {
                                        missionDocument.WriteEndElement(); // LinkedColumns 
                                    }

                                    // write all given data-rows
                                    // use a for-loop here, to save the current rowIndex
                                    for (int rowIndex = 0; rowIndex < packageOn.Rows.Count; rowIndex++)
                                    {
                                        DataRow rowOn = packageOn.Rows[rowIndex];

                                        missionDocument.WriteStartElement("Row");
                                        // ... defining the every row, column by column
                                        foreach (MissionPluginPackageColumn columnOn in packageOn.Columns)
                                        {
                                            missionDocument.WriteStartElement(columnOn.ColumnName);

                                            // if the current cell has a valid value...
                                            if (false == rowOn.IsNull(columnOn))
                                            {
                                                missionDocument.WriteCData(rowOn[columnOn].ToString());
                                            }
                                            // if the current cell has a NULLDB-value
                                            else
                                            {
                                                if (true == columnOn.HasCellReference)
                                                {
                                                    missionDocument.WriteStartElement("GetValueByLoop");
                                                    missionDocument.WriteAttributeString("FromLoopID", columnOn.CellReference.LoopID.ToString());
                                                    missionDocument.WriteAttributeString("FromColumn", columnOn.CellReference.ColumnName);
                                                    missionDocument.WriteEndElement(); // GetValueByLoop
                                                }
                                            }
                                            missionDocument.WriteEndElement(); // columnOn.ColumnName
                                        }
                                        missionDocument.WriteEndElement(); // "Row"
                                    }
                                    missionDocument.WriteEndElement(); // "Data"                                
                                }
                                missionDocument.WriteEndElement(); // "Package"
                            }
                        }
                        missionDocument.WriteEndElement(); // directionOn              
                    }
                    missionDocument.WriteEndElement(); // end "Plugin"
                }
            }
        }
    }
}
